package concurrency.part2.task2;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Buffer<T> {
    private final int MAX_SIZE;
    private int size;
    private int startIndex;
    private int endIndex;
    private List<T> buffer;
    private Condition addCondition, getCondition;
    private Lock lock;

    public Buffer(int maxSize) {
        this.lock = new ReentrantLock();
        this.addCondition = lock.newCondition();
        this.getCondition = lock.newCondition();
        buffer = new ArrayList<T>(maxSize);
        for (int i = 0; i < maxSize; i++)
            buffer.add(null);
        MAX_SIZE = maxSize;
        size = 0;
        startIndex = endIndex = 0;
    }

    public void add(T t) {
        try {
            lock.lock();
            while (isFull()) {
                try {
                    addCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            endIndex = nextIndex(endIndex);
            buffer.set(endIndex,t);
            getCondition.signal();
            size++;
        } finally {
            lock.unlock();
        }
    }

    public T get() {
        try {
            lock.lock();
            while (isEmpty()) {
                try {
                    getCondition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            T res = buffer.get(startIndex);
            startIndex = nextIndex(startIndex);
            size--;
            addCondition.signal();
            return res;
        } finally {
            lock.unlock();
        }
    }

    public T peek() {
        try {
            lock.lock();
            if (isEmpty())
                return null;
            else
                return buffer.get(startIndex);
        } finally {
            lock.unlock();
        }

    }

    private boolean isEmpty() {
        return size == 0;
    }

    private boolean isFull() {
        return size == MAX_SIZE;
    }

    private int nextIndex(int index) {
        return (index + 1) % MAX_SIZE;
    }
}
